iT邦幫忙

2026 iThome 鐵人賽

DAY 10
1
自我挑戰組

韌體工程師的不只 0x10 個問題系列 第 10

Day 10 - 指標與整數間的型別轉換

  • 分享至 

  • xImage
  •  

如果給定一個值如 0x67a9,這到底是「指標」還是「(十六進制的)整數」型別呢?

答案是兩種都有可能,而且兩型別之間可以互相轉換,本篇文章就來細細探討這是怎麼回事。

整數轉換成指標

以下列程式碼而言:

int main(){
    int *ptr = (int *)0x67a9;
    *ptr = 0xaa55;
}

首先來看這個 main() 函數中的第一行:

  • int *ptr:宣告一個名為 ptr 的指標變數,指標指向一個整數型別。
  • (int *)0x67a90x67a9 本來是一個十六進位的數字,強制轉換成一個記憶體位址的值,該記憶體位址裝的是整數型別。

總之,現在 0x67a9 是一個記憶體位址,該記憶體位址存著一個整數,而指向該記憶體位址的指標變數名為 ptr

       +-------------+
ptr -> | int 型別變數 |
       +-------------+
       記憶體位址 0x67a9 

接著來看第二行:

*ptr = 0xaa55;
  • *ptr:為 ptr 這個變數解索引。
  • 0xaa55:要放進 ptr 指向之記憶體位址的值。

所以會變成這樣:

       +------------+
ptr -> |   0xaa55   |
       +------------+
      記憶體位址 0x67a9 

整體而言,本程式碼的重點是原為整數的 0x67a9(int *) 做了型別轉換,變成一個記憶體位址,然後再把 0xaa55 這個值放進該記憶體位址。

這邊未完待續,到第三段我們還會再回頭看這樣寫有什麼問題。

指標轉換成整數

除了可以將整數轉為指標,也可以把指標轉為整數。

以下列範例而言:

int a = 100;
int *ptr = &a; // ptr 是指向 a 變數記憶體位址的指標

int address_value = (int)ptr; // 把 ptr 指標變數型別改為 int

看起來很平常對吧?但事實上這樣寫非常危險!

因為在現代 64 位元電腦中,整數型別只佔 32 位元、但指標佔了 64 位元,佔不同位元的型別互相轉換可能會讓資料本身跑掉,因此要用 intptr_t 或無正負號的 uintptr_t 比較安全,範例如下:

#include <iostream>
#include <stdint.h> // uintptr_t 放在 <stdint.h> 裡面

int main()
{
    int a = 100;
    int *ptr = &a;
    
    // 因為指標不會是負數,因此用 uintptr_t 而非 intptr_t
    uintptr_t address_value = (uintptr_t)ptr;

    return 0;
}

intptr_tuintptr_t 會自動調配位元,避免資料異動。

思考:難道整數轉指標就安全?

指標轉整數會因為型別位元數不同而危險,但整數轉指標難道不會有相同問題?

其實也不安全!因此一樣要派出 uintptr_t,且轉換時使用 reinterpret_cast,使程式的閱讀者能更清楚看出是在進行風險較高的型別轉換:

#include <iostream>
#include <cstdint> // uintptr_t 在 <cstdint> 裡面

int main() {
    uintptr_t address = 0x67a9;  // 0x67a9 依然是一個整數型別,但用 uintptr_t 取代 int 

    // 用 reinterpret_cast 將整數變數轉換為指標
    // 另外用 volatile 確保編譯器處理 ptr 指向的 int 時,不會使用暫存檔
    volatile int* ptr = reinterpret_cast<volatile int*>(address);

    *ptr = 0xaa55;  // 解引用並寫入數值
    
    return 0;
}

其中這行比較關鍵,可以拆開來看:

volatile int* ptr = reinterpret_cast<volatile int*>(address);
  • volatile int* ptrptr 是一個指向 volatile int 的指標。
  • reinterpret_cast:表示要把後面的 addressuintptr_t 型別強制轉成 <volatile int*> 型別。

所以整體而言就是「reinterpret_cast 把原為 uintptr_t 型別的變數 address 轉為指向 volatile int* 型別之指標 ptr」,後面再解索引說指標指向的值為 0xaa55

參考資料

  1. Warning: Cast From Integer to Pointer of Different Size in C++ (GeeksforGeeks)
  2. reinterpret_cast in C++ | Type Casting operators (GeeksforGeeks)
  3. 韌體工程師的0x10個問題

上一篇
Day 9-萬惡的指標
下一篇
Day 11-位元遮罩
系列文
韌體工程師的不只 0x10 個問題19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言